using System;

namespace Oops.Xna.Framework
{
	/// <summary>
	/// Contains commonly used precalculated values and numeric calculations.
	/// </summary>
	public static class MathExtension
	{
		/// <summary>
		/// Represents the smallest positive <see cref="System.Single"/> greater than zero. This field is constant.
		/// </summary>
		public const float Epsilon = 1.0e-6f;

		/// <summary>
		/// Returns a 1.0f / Math.Sqrt(value) approximation.
		/// </summary>
		/// <param name="value">The source value.</param>
		/// <returns>A <see cref="System.Single"/> representing an approximation of the reciprocal of the square-root of <paramref name="value"/>.</returns>
		public unsafe static float InvSqrt(float value)
		{
			float half = 0.5f * value;

			int i = *(int*)&value;
			i = 0x5f375a86 - (i >> 1);

			value = *(float*)&i;
			value = value * (1.5f - half * value * value);

			return value;
		} 

		/// <summary>
		/// Restricts a value to be within a specified range.
		/// </summary>
		/// <param name="value">The value to clamp.</param>
		/// <param name="min">The minimum value.</param>
		/// <param name="max">The maximum value.</param>
		/// <returns>Returns the clamped value.</returns>
		public static int Clamp(int value, int min, int max)
		{
			// Clamp the value be the min and max values.
			value = value > max ? max : value;
			value = value < min ? min : value;

			// Return the clamped value.
			return value;
		}

		/// <summary>
		/// Determines if a value is odd.
		/// </summary>
		/// <param name="value">The source value.</param>
		/// <returns>Return <c>true</c> if <paramref name="value"/> is odd. Otherwise, it returns <c>false</c>.</returns>
		public static bool Odd(int value)
		{
			return (value & 0x00000001) == 0x00000001;
		}

		/// <summary>
		/// Returns the base 2 logarithm of a specified number.
		/// </summary>
		/// <param name="value">A number whose logarithm is to be found.</param>
		/// <returns>The base 2 logarithm of <paramref name="value"/>.</returns>
		public static int Log2(int value)
		{
			return (int)(Math.Log10(value) / Math.Log10(2));
		}

		/// <summary>
		/// Determines if a value is a power of 2.
		/// </summary>
		/// <param name="value">The source value.</param>
		/// <returns>Return <c>true</c> if <paramref name="value"/> is a power of 2. Otherwise, it returns <c>false</c>.</returns>
		public static bool PowerOfTwo(int value)
		{
			return value != 0 && (value & (value - 1)) == 0;
		}

		/// <summary>
		/// Rounds the value to the nearest increment.
		/// </summary>
		/// <param name="value">The source <see cref="String.Single"/>.</param>
		/// <param name="increment">The increment to round to.</param>
		/// <returns>Returns the value rounds to the nearest increment.</returns>
		public static float RoundTo(float value, float increment)
		{
			return (float)Math.Round(value / increment) * increment;
		}

		/// <summary>
		/// Determines if the values specified are only an epsilon of being equal.
		/// </summary>
		/// <param name="value0">The first source value.</param>
		/// <param name="value1">The second source value.</param>
		/// <returns><b>true</b> if <paramref name="value0"/> and <paramref name="value1"/> are an epsilon or less of different.  Otherwise, returns <b>false</b>.</returns>
		public static bool IsAlmostEqual(float value0, float value1)
		{
			if (Math.Abs(value0 - value1) < MathExtension.Epsilon)
			{
				return true;
			}

			return false;
		}
	}
}
